#include <gtest/gtest.h>

#include "MyIterator.h"
#include <helper/iter/Skip.h>
#include <helper/unittest/Tracer.h>

using namespace helper;
using namespace helper::iter;

TEST(SkipTest, testNext){
  
  //next
  ASSERT_EQ(0, MyIterator<int>(0, 10).skip(0).next().value());
  ASSERT_EQ(1, MyIterator<int>(0, 10).skip(1).next().value());
  ASSERT_EQ(std::nullopt, MyIterator<int>(0, 10).skip(10).next());
  ASSERT_EQ(std::nullopt, MyIterator<int>(0, 10).skip(100).next());


  //nth
  ASSERT_EQ(0, MyIterator<int>(0, 10).skip(0).nth(0).value());
  ASSERT_EQ(1, MyIterator<int>(0, 10).skip(0).nth(1).value());
  ASSERT_EQ(1, MyIterator<int>(0, 10).skip(1).nth(0).value());

  ASSERT_EQ(std::nullopt, MyIterator<int>(0, 10).skip(10).nth(0));
  ASSERT_EQ(std::nullopt, MyIterator<int>(0, 10).skip(0).nth(10));
  ASSERT_EQ(std::nullopt, MyIterator<int>(0, 10).skip(2).nth(10));
  ASSERT_EQ(std::nullopt, MyIterator<int>(0, 10).skip(10).nth(3));

  //count
  ASSERT_EQ((size_t)10, MyIterator<int>(0, 10).skip(0).count());
  ASSERT_EQ((size_t)9, MyIterator<int>(0, 10).skip(1).count());
  ASSERT_EQ((size_t)7, MyIterator<int>(0, 10).skip(3).count());
  ASSERT_EQ((size_t)1, MyIterator<int>(0, 10).skip(9).count());
  ASSERT_EQ((size_t)0, MyIterator<int>(0, 10).skip(10).count());
}

TEST(SkipTest, testTryFold__All){
  {
    bool ok = MyIterator<int>(0, 10).skip(0)
        .tryFold<ControlFlowFlag, ControlFlowOpCode>(ControlFlowOpCode::BREAK, 
          [](const auto & init, const auto & v){
            return (v >= 0) ? ControlFlowFlag::Continue() : ControlFlowFlag::Break();
          }).isContinue();
    ASSERT_TRUE(ok);

    ASSERT_TRUE(MyIterator<int>(0, 10).skip(0).all([](const auto & v){ return v>= 0;}));

  }
  {
    bool ok = MyIterator<int>(0, 10).skip(3)
        .tryFold<ControlFlowFlag, ControlFlowOpCode>(ControlFlowOpCode::BREAK, 
          [](const auto & init, const auto & v){
            return (v >= 0) ? ControlFlowFlag::Continue() : ControlFlowFlag::Break();
          }).isContinue();
    ASSERT_TRUE(ok);

    ASSERT_TRUE(MyIterator<int>(0, 10).skip(3).all([](const auto & v){ return v>= 0;}));
  }
  {
    bool ok = MyIterator<int>(0, 10).skip(10) 
        .tryFold<ControlFlowFlag, ControlFlowOpCode>(ControlFlowOpCode::BREAK, 
          [](const auto & init, const auto & v){
            return (v >= 0) ? ControlFlowFlag::Continue() : ControlFlowFlag::Break();
          }).isContinue();
    ASSERT_TRUE(ok);
    ASSERT_TRUE(MyIterator<int>(0, 10).skip(10).all([](const auto & v){ return v>= 0;}));
  }
  {
    bool ok = MyIterator<int>(0, 10).skip(11)
        .tryFold<ControlFlowFlag, ControlFlowOpCode>(ControlFlowOpCode::BREAK, 
          [](const auto & init, const auto & v){
            return (v >= 0) ? ControlFlowFlag::Continue() : ControlFlowFlag::Break();
          }).isContinue();
    ASSERT_TRUE(ok);
    ASSERT_TRUE(MyIterator<int>(0, 10).skip(11).all([](const auto & v){ return v>= 0;}));
  }
  {
    bool ok = MyIterator<int>(0, 10).skip(100)
        .tryFold<ControlFlowFlag, ControlFlowOpCode>(ControlFlowOpCode::BREAK, 
          [](const auto & init, const auto & v){
            return (v >= 0) ? ControlFlowFlag::Continue() : ControlFlowFlag::Break();
          }).isContinue();
    ASSERT_TRUE(ok);
    ASSERT_TRUE(MyIterator<int>(0, 10).skip(100).all([](const auto & v){ return v>= 0;}));
  }
}

TEST(SkipTest, testTryFold__Any){
  {
    bool ok = MyIterator<int>(0, 10).skip(0)
        .tryFold<ControlFlowFlag, ControlFlowOpCode>(ControlFlowOpCode::CONTINUE, 
          [](const auto & init, const auto & v){
            return (v >= 0) ? ControlFlowFlag::Break() : ControlFlowFlag::Continue();
          }).isBreak();
    ASSERT_TRUE(ok);
    ASSERT_TRUE(MyIterator<int>(0, 10).skip(0).any([](const auto & v){ return v>= 0;}));
  }
  {
    bool ok = MyIterator<int>(0, 10).skip(3)
        .tryFold<ControlFlowFlag, ControlFlowOpCode>(ControlFlowOpCode::CONTINUE, 
          [](const auto & init, const auto & v){
            return (v >= 0) ? ControlFlowFlag::Break() : ControlFlowFlag::Continue();
          }).isBreak();
    ASSERT_TRUE(ok);
    ASSERT_TRUE(MyIterator<int>(0, 10).skip(3).any([](const auto & v){ return v>= 0;}));
  }
  {
    bool ok = MyIterator<int>(0, 10).skip(10)
        .tryFold<ControlFlowFlag, ControlFlowOpCode>(ControlFlowOpCode::CONTINUE, 
          [](const auto & init, const auto & v){
            return (v >= 0) ? ControlFlowFlag::Break() : ControlFlowFlag::Continue();
          }).isBreak();
    ASSERT_FALSE(ok);
    ASSERT_FALSE(MyIterator<int>(0, 10).skip(10).any([](const auto & v){ return v>= 0;}));
  }
  {
    bool ok = MyIterator<int>(0, 10).skip(11)
        .tryFold<ControlFlowFlag, ControlFlowOpCode>(ControlFlowOpCode::CONTINUE, 
          [](const auto & init, const auto & v){
            return (v >= 0) ? ControlFlowFlag::Break() : ControlFlowFlag::Continue();
          }).isBreak();
    ASSERT_FALSE(ok);
    ASSERT_FALSE(MyIterator<int>(0, 10).skip(11).any([](const auto & v){ return v>= 0;}));
  }
  {
    bool ok = MyIterator<int>(0, 10).skip(100)
        .tryFold<ControlFlowFlag, ControlFlowOpCode>(ControlFlowOpCode::CONTINUE, 
          [](const auto & init, const auto & v){
            return (v >= 0) ? ControlFlowFlag::Break() : ControlFlowFlag::Continue();
          }).isBreak();
    ASSERT_FALSE(ok);
    ASSERT_FALSE(MyIterator<int>(0, 10).skip(100).any([](const auto & v){ return v>= 0;}));
  }
}

TEST(SkipTest, testFold){
  {
    EXPECT.reset()
      .operation("[0,10).skip(0)->v:0")
      .operation("[0,10).skip(0)->v:1")
      .operation("[0,10).skip(0)->v:2")
      .operation("[0,10).skip(0)->v:3")
      .operation("[0,10).skip(0)->v:4")
      .operation("[0,10).skip(0)->v:5")
      .operation("[0,10).skip(0)->v:6")
      .operation("[0,10).skip(0)->v:7")
      .operation("[0,10).skip(0)->v:8")
      .operation("[0,10).skip(0)->v:9")
    ;
    TRACER.reset();
    MyIterator<int>(0, 10).skip(0)
      .foreach([](const auto & v) {
        TRACER.operation("[0,10).skip(0)->v:" + std::to_string(v));
      });
    ASSERT_EQ(EXPECT, TRACER);
    
    ASSERT_EQ((size_t)10, MyIterator<int>(0, 10).skip(0).count());
  }

  {
    EXPECT.reset()
      .operation("[0,10).skip(3)->v:3")
      .operation("[0,10).skip(3)->v:4")
      .operation("[0,10).skip(3)->v:5")
      .operation("[0,10).skip(3)->v:6")
      .operation("[0,10).skip(3)->v:7")
      .operation("[0,10).skip(3)->v:8")
      .operation("[0,10).skip(3)->v:9")
    ;
    TRACER.reset();
    MyIterator<int>(0, 10).skip(3)
      .foreach([](const auto & v) {
        TRACER.operation("[0,10).skip(3)->v:" + std::to_string(v));
      });
    ASSERT_EQ(EXPECT, TRACER);

    ASSERT_EQ((size_t)(10-3), MyIterator<int>(0, 10).skip(3).count());
  }

  {
    EXPECT.reset()
    ;
    TRACER.reset();
    MyIterator<int>(0, 10).skip(10)
      .foreach([](const auto & v) {
        TRACER.operation("[0,10).skip(10)->v:" + std::to_string(v));
      });
    ASSERT_EQ(EXPECT, TRACER);

    ASSERT_EQ((size_t)(10-10), MyIterator<int>(0, 10).skip(10).count());
  }

  {
    EXPECT.reset()
    ;
    TRACER.reset();
    MyIterator<int>(0, 10).skip(11)
      .foreach([](const auto & v) {
        TRACER.operation("[0,10).skip(11)->v:" + std::to_string(v));
      });
    ASSERT_EQ(EXPECT, TRACER);

    ASSERT_EQ((size_t)(0), MyIterator<int>(0, 10).skip(11).count());
  }

  {
    EXPECT.reset()
    ;
    TRACER.reset();
    MyIterator<int>(0, 10).skip(100)
      .foreach([](const auto & v) {
        TRACER.operation("[0,10).skip(100)->v:" + std::to_string(v));
      });
    ASSERT_EQ(EXPECT, TRACER);

    ASSERT_EQ((size_t)(0), MyIterator<int>(0, 10).skip(100).count());
  }

}

TEST(SkipTest, testAdvanceBy){
  
  ASSERT_TRUE(         MyIterator<int>(0, 10).skip(0).advanceBy(0));
  ASSERT_TRUE(         MyIterator<int>(0, 10).skip(5).advanceBy(0));
  ASSERT_TRUE(         MyIterator<int>(0, 10).skip(8).advanceBy(0));
  ASSERT_TRUE(         MyIterator<int>(0, 10).skip(9).advanceBy(0));
  ASSERT_TRUE(         MyIterator<int>(0, 10).skip(10).advanceBy(0));
  ASSERT_TRUE(         MyIterator<int>(0, 10).skip(100).advanceBy(0));


  ASSERT_TRUE(         MyIterator<int>(0, 10).skip(5).advanceBy(1));
  ASSERT_TRUE(         MyIterator<int>(0, 10).skip(5).advanceBy(3));
  ASSERT_TRUE(         MyIterator<int>(0, 10).skip(5).advanceBy(4));
  ASSERT_TRUE(         MyIterator<int>(0, 10).skip(5).advanceBy(5));

  //advance failed, Error(advance): 
  ASSERT_FALSE(        MyIterator<int>(0, 10).skip(5).advanceBy(6));
  ASSERT_EQ((size_t)5, MyIterator<int>(0, 10).skip(5).advanceBy(6).error());
  ASSERT_EQ((size_t)5, MyIterator<int>(0, 10).skip(5).advanceBy(100).error());


  ASSERT_EQ((size_t)0, MyIterator<int>(0, 10).skip(20).advanceBy(1).error());
  ASSERT_EQ((size_t)0, MyIterator<int>(0, 10).skip(20).advanceBy(1).error());
  ASSERT_EQ((size_t)0, MyIterator<int>(0, 10).skip(20).advanceBy(100).error());
  ASSERT_EQ((size_t)0, MyIterator<int>(0, 10).skip(20).advanceBy(100).error());
}
